/*******************************************************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Nicolas DAURES
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *******************************************************************************************************************/


/****************************************************************
 * Particle
 ****************************************************************/

// Particle
function Particle()
{
    //=======================
    // Fields
    //=======================

    this.m_ParticleEmitter =	null;
    this.m_v2Forces =			new b2Vec2(0, 0);
    this.m_v2Speed =			new b2Vec2(0, 0);
    this.m_v2Position = 		new b2Vec2(0, 0);
    this.m_v2ScaleInit = 		new b2Vec2(1, 1);
    this.m_v2Scale =	 		new b2Vec2(1, 1);
    this.m_fAngularSpeed =		0;
    this.m_fOrientation =		0;
    this.m_fLifeInit =			1;
    this.m_fLife =				1;
};


//=======================
// Operations
//=======================

Particle.prototype.update = function(a_fdt)
{
    this.m_v2Speed.x += this.m_v2Forces.x * a_fdt;
    this.m_v2Speed.y += this.m_v2Forces.y * a_fdt;
    this.m_v2Position.x += this.m_v2Speed.x * a_fdt;
    this.m_v2Position.y += this.m_v2Speed.y * a_fdt;

    this.m_fOrientation += this.m_fAngularSpeed * a_fdt;

    this.m_v2Forces.x = 0;
    this.m_v2Forces.y = 0;

    this.m_fLife -= a_fdt;

    if (this.m_ParticleEmitter.m_bScaleInTime)
    {
        var fRatio = this.m_fLife / this.m_fLifeInit;
        this.m_v2Scale.x = this.m_v2ScaleInit.x * fRatio + this.m_ParticleEmitter.m_fScaleEnd * (1 - fRatio);
        this.m_v2Scale.y = this.m_v2ScaleInit.y * fRatio + this.m_ParticleEmitter.m_fScaleEnd * (1 - fRatio);
    }
}

Particle.prototype.applyForce = function(a_v2Force)
{
    this.m_v2Forces.x += a_v2Force.x;
    this.m_v2Forces.y += a_v2Force.y;
}


/****************************************************************
 * Particle Emitter
 ****************************************************************/

var EParticleEmitterShape = {POINT:0, DISK:1, RECTANGLE:2}
var EParticleShape = {DISK:0, STROKE_DISK:1, RECTANGLE:2}

// Particle emitter
function ParticleEmitter()
{
    //=======================
    // Fields
    //=======================

    this.m_ParticleSystem =		    null;
    this.m_SceneNode =			    new SceneNode();
    this.m_aParticles =			    new Array();

    this.m_fSpeedMin =			    10;
    this.m_fSpeedMax =			    20;
    this.m_fAngularSpeedMin = 	    0;
    this.m_fAngularSpeedMax =	    0;
    this.m_fLifeMin = 			    3;
    this.m_fLifeMax = 			    5;
    this.m_fScaleMin = 			    10;
    this.m_fScaleMax = 			    20;
    this.m_fScaleEnd = 			    0;
    this.m_fAngleMin = 			    0;
    this.m_fAngleMax = 			    0;
    this.m_fEmitAngle = 			0.2;

    this.m_ColorStart =             new Color(255, 255, 255, 255);
    this.m_ColorEnd =               new Color(0, 0, 0, 0);

    this.m_eEmitShape =             EParticleEmitterShape.POINT;
    this.m_fEmitRadius =            10;
    this.m_v2EmitArea =             new b2Vec2(10, 10);

    this.m_eShape =                 EParticleShape.DISK;

    this.m_bScaleInTime =			true;
    this.m_bFollowEmitter =			false;

    this.m_fTimeSinceLastPulse =    0;
    this.m_fFrequency =			    0;

    this.m_Blending =				"lighter";

    this.m_bIsLaunched = 			false;
};


//=======================
// Accessors
//=======================

ParticleEmitter.prototype.getSceneNode = function()
{
    return this.m_SceneNode;
}

ParticleEmitter.prototype.getSpeedMin = function()
{
    return this.m_fSpeedMin;
}

ParticleEmitter.prototype.setSpeedMin = function(a_fSpeedMin)
{
    this.m_fSpeedMin = a_fSpeedMin;
}

ParticleEmitter.prototype.getSpeedMax = function()
{
    return this.m_fSpeedMax;
}

ParticleEmitter.prototype.setSpeedMax = function(a_fSpeedMax)
{
    this.m_fSpeedMax = a_fSpeedMax;
}

ParticleEmitter.prototype.getAngularSpeedMin = function()
{
    return this.m_fAngularSpeedMin;
}

ParticleEmitter.prototype.setAngularSpeedMin = function(a_fAngularSpeedMin)
{
    this.m_fAngularSpeedMin = a_fAngularSpeedMin;
}

ParticleEmitter.prototype.getAngularSpeedMax = function()
{
    return this.m_fAngularSpeedMax;
}

ParticleEmitter.prototype.setAngularSpeedMax = function(a_fAngularSpeedMax)
{
    this.m_fAngularSpeedMax = a_fAngularSpeedMax;
}

ParticleEmitter.prototype.getLifeMin = function()
{
    return this.m_fLifeMin;
}

ParticleEmitter.prototype.setLifeMin = function(a_fLifeMin)
{
    this.m_fLifeMin = a_fLifeMin;
}

ParticleEmitter.prototype.getLifeMax = function()
{
    return this.m_fLifeMax;
}

ParticleEmitter.prototype.setLifeMax = function(a_fLifeMax)
{
    this.m_fLifeMax = a_fLifeMax;
}

ParticleEmitter.prototype.getAngleMin = function()
{
    return this.m_fAngleMin;
}

ParticleEmitter.prototype.setAngleMin = function(a_fAngleMin)
{
    this.m_fAngleMin = a_fAngleMin;
}

ParticleEmitter.prototype.getAngleMax = function()
{
    return this.m_fAngleMax;
}

ParticleEmitter.prototype.setAngleMax = function(a_fAngleMax)
{
    this.m_fAngleMax = a_fAngleMax;
}

ParticleEmitter.prototype.getScaleMin = function()
{
    return this.m_fScaleMin;
}

ParticleEmitter.prototype.setScaleMin = function(a_fScaleMin)
{
    this.m_fScaleMin = a_fScaleMin;
}

ParticleEmitter.prototype.getScaleMax = function()
{
    return this.m_fScaleMax;
}

ParticleEmitter.prototype.setScaleMax = function(a_fScaleMax)
{
    this.m_fScaleMax = a_fScaleMax;
}

ParticleEmitter.prototype.getScaleEnd = function()
{
    return this.m_fScaleEnd;
}

ParticleEmitter.prototype.setScaleEnd = function(a_fScaleEnd)
{
    this.m_fScaleEnd = a_fScaleEnd;
}

ParticleEmitter.prototype.getShape = function()
{
    return this.m_eShape;
}

ParticleEmitter.prototype.setShape = function(a_eShape)
{
    this.m_eShape = a_eShape;
}

ParticleEmitter.prototype.getEmitAngle = function()
{
    return this.m_fEmitAngle;
}

ParticleEmitter.prototype.setEmitAngle = function(a_fEmitAngle)
{
    this.m_fEmitAngle = a_fEmitAngle;
}

ParticleEmitter.prototype.getEmitShape = function()
{
    return this.m_eEmitShape;
}

ParticleEmitter.prototype.setEmitShape = function(a_eEmitShape)
{
    this.m_eEmitShape = a_eEmitShape;
}

ParticleEmitter.prototype.getEmitRadius = function()
{
    return this.m_fEmitRadius;
}

ParticleEmitter.prototype.setEmitRadius = function(a_fEmitRadius)
{
    this.m_fEmitRadius = a_fEmitRadius;
}

ParticleEmitter.prototype.getEmitArea = function()
{
    return this.m_v2EmitArea;
}

ParticleEmitter.prototype.setEmitArea = function(a_v2EmitArea)
{
    this.m_v2EmitArea.x = a_v2EmitArea.x;
    this.m_v2EmitArea.y = a_v2EmitArea.y;
}

ParticleEmitter.prototype.getColorStart = function()
{
    return this.m_ColorStart;
}

ParticleEmitter.prototype.setColorStart = function(a_ColorStart)
{
    this.m_ColorStart = a_ColorStart;
}

ParticleEmitter.prototype.getColorEnd = function()
{
    return this.m_ColorEnd;
}

ParticleEmitter.prototype.setColorEnd = function(a_ColorEnd)
{
    this.m_ColorEnd = a_ColorEnd;
}

ParticleEmitter.prototype.isScaleInTime = function()
{
    return this.m_bScaleInTime;
}

ParticleEmitter.prototype.setScaleInTime = function(a_bScaleInTime)
{
    this.m_bScaleInTime = a_bScaleInTime;
}

ParticleEmitter.prototype.isFollowEmitter = function()
{
    return this.m_bFollowEmitter;
}

ParticleEmitter.prototype.setFollowEmitter = function(a_bFollowEmitter)
{
    this.m_bFollowEmitter = a_bFollowEmitter;
}

ParticleEmitter.prototype.getFrequency = function()
{
    return this.m_fFrequency;
}

ParticleEmitter.prototype.setFrequency = function(a_fFrequency)
{
    this.m_fFrequency = a_fFrequency;
}

ParticleEmitter.prototype.getBlending = function()
{
    return this.m_Blending;
}

ParticleEmitter.prototype.setBlending = function(a_Blending)
{
    this.m_Blending = a_Blending;
}


//=======================
// Operations
//=======================

ParticleEmitter.prototype.update = function()
{
    var fdt = TimeEngine.getInstance().getDeltaTime();

    // Remove killed particles
    var particleToRemove = new Array();
    for (var i = 0; i < this.m_aParticles.length; i++)
    {
        var particle = this.m_aParticles[i];
        if (particle.m_fLife < 0)
        {
            particleToRemove.push(particle);
            this.m_ParticleSystem.pushParticle(particle);
        }
    }
    for (var i = 0; i < particleToRemove.length; i++)
    {
        var particle = particleToRemove[i];
        for (var j = 0; j < this.m_aParticles.length; j++)
        {
            if (this.m_aParticles[j] == particle)
            {
                this.m_aParticles.splice(j, 1);
            }
        }
    }

    // Update particles
    for (var i = 0; i < this.m_aParticles.length; i++)
    {
        var particle = this.m_aParticles[i];
        particle.update(fdt);
    }

    // Draw particles
    this.draw();
}

ParticleEmitter.prototype.draw = function()
{
    var v2CamPos = CameraEngine.getInstance().m_SceneNode.m_v2Pos;
    var v2Position = new b2Vec2(0, 0);
    var v2Scale = new b2Vec2(0, 0);
    var fOrientation = 0;
    var fRatio = 0;
    var f1mRatio = 0;

    // Draw particles
    if (this.m_ParticleSystem.m_Image != null)
    {
        for (var i = 0; i < this.m_aParticles.length; i++)
        {
            var particle = this.m_aParticles[i];

            var v2Position = new b2Vec2(particle.m_v2Position.x, particle.m_v2Position.y);
            if (this.m_bFollowEmitter)
            {
                v2Position.x += this.m_SceneNode.m_v2WorldPos.x;
                v2Position.y += this.m_SceneNode.m_v2WorldPos.y;
            }

            var v2PosInScreen = convertScenePosToScreenPos(v2Position, v2CamPos);
            v2Scale.x = particle.m_v2Scale.x;
            v2Scale.y = particle.m_v2Scale.y;
            fOrientation = particle.m_fOrientation;

            context.save();
            context.globalCompositeOperation = this.m_Blending;
            context.translate(v2PosInScreen.x, v2PosInScreen.y);
            context.rotate(fOrientation);
            context.scale(v2Scale.x, v2Scale.y);
            context.drawImage(this.m_ParticleSystem.m_Image, -0.5, -0.5, 1, 1);
            context.restore();
        }
    }
    else
    {
        for (var i = 0; i < this.m_aParticles.length; i++)
        {
            var particle = this.m_aParticles[i];

            var v2Position = new b2Vec2(particle.m_v2Position.x, particle.m_v2Position.y);
            if (this.m_bFollowEmitter)
            {
                v2Position.x += this.m_SceneNode.m_v2WorldPos.x;
                v2Position.y += this.m_SceneNode.m_v2WorldPos.y;
            }

            var v2PosInScreen = convertScenePosToScreenPos(v2Position, v2CamPos);
            v2Scale.x = particle.m_v2Scale.x;
            v2Scale.y = particle.m_v2Scale.y;
            fOrientation = particle.m_fOrientation;
            fRatio = particle.m_fLife / particle.m_fLifeInit;
            f1mRatio = 1 - fRatio;

            context.save();
            context.globalCompositeOperation = this.m_Blending;
            context.translate(v2PosInScreen.x, v2PosInScreen.y);
            context.rotate(fOrientation);
            context.scale(v2Scale.x, v2Scale.y);
            var r = Math.floor(this.m_ColorEnd.r * f1mRatio + this.m_ColorStart.r * fRatio);
            var g = Math.floor(this.m_ColorEnd.g * f1mRatio + this.m_ColorStart.g * fRatio);
            var b = Math.floor(this.m_ColorEnd.b * f1mRatio + this.m_ColorStart.b * fRatio);
            var a = (this.m_ColorEnd.a * f1mRatio + this.m_ColorStart.a * fRatio) / 255;
            switch(this.m_eShape)
            {
                case EParticleShape.DISK:
                    context.beginPath();
                    context.arc(0, 0, 1, 2 * Math.PI, false);
                    context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
                    context.fill();
                    break;

                case EParticleShape.STROKE_DISK:
                    context.beginPath();
                    context.arc(0, 0, 1, 2 * Math.PI, false);
                    context.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
                    context.stroke();
                    break;

                default:
                    context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
                    context.fillRect(-0.5, -0.5, 1, 1);
                    break;
            }
            context.restore();
        }
    }
}

ParticleEmitter.prototype.launch = function()
{
    this.m_bIsLaunched = true;
}

ParticleEmitter.prototype.stop = function()
{
    this.m_bIsLaunched = false;
}

ParticleEmitter.prototype.emit = function(a_fdt)
{
    if (this.m_bIsLaunched)
    {
        this.m_fTimeSinceLastPulse += a_fdt;
        var iParticleCount = Math.floor(this.m_fTimeSinceLastPulse * this.m_fFrequency);
        if (iParticleCount > 0)
        {
            this.pulse(iParticleCount);
            this.m_fTimeSinceLastPulse -= iParticleCount / this.m_fFrequency;
        }
    }
}

ParticleEmitter.prototype.pulse = function(a_iParticleCount)
{
    for (var i = 0; i < a_iParticleCount; i++)
    {
        var particle = this.m_ParticleSystem.popParticle();
        if (particle == null)
        {
            return;
        }
        this.m_aParticles.push(particle);

        // Initialize position
        var v2EmitterPos = this.m_SceneNode.m_v2Pos;
        switch(this.m_eEmitShape)
        {
            case EParticleEmitterShape.DISK:
                var fLenght = Math.random() * this.m_fEmitRadius;
                var v2Dir = new b2Vec2(0, 1);
                v2Dir = rotateVector(v2Dir, randomf(0, 2 * Math.PI));
                if (this.m_bFollowEmitter)
                {
                    particle.m_v2Position.x = v2Dir.x * fLenght;
                    particle.m_v2Position.y = v2Dir.y * fLenght;
                }
                else
                {
                    particle.m_v2Position.x = v2Dir.x * fLenght + v2EmitterPos.x;
                    particle.m_v2Position.y = v2Dir.y * fLenght + v2EmitterPos.y;
                }
                break;
            case EParticleEmitterShape.RECTANGLE:
                var fEmitHalfWidth = this.m_v2EmitArea.x * 0.5;
                var fEmitHalfHeight = this.m_v2EmitArea.y * 0.5;
                if (this.m_bFollowEmitter)
                {
                    particle.m_v2Position.x = randomf(-fEmitHalfWidth, fEmitHalfWidth);
                    particle.m_v2Position.y = randomf(-fEmitHalfHeight, fEmitHalfHeight);
                }
                else
                {
                    particle.m_v2Position.x = randomf(-fEmitHalfWidth, fEmitHalfWidth) + v2EmitterPos.x;
                    particle.m_v2Position.y = randomf(-fEmitHalfHeight, fEmitHalfHeight) + v2EmitterPos.y;
                }
                break;
            default :
                if (this.m_bFollowEmitter)
                {
                    particle.m_v2Position.x = 0;
                    particle.m_v2Position.y = 0;
                }
                else
                {
                    particle.m_v2Position.x = v2EmitterPos.x;
                    particle.m_v2Position.y = v2EmitterPos.y;
                }
                break;
        }

        // Initialize speed
        var fSpeedInit = randomf(this.m_fSpeedMin, this.m_fSpeedMax);
        var v2SpeedInit = new b2Vec2(0, 1);
        v2SpeedInit = rotateVector(v2SpeedInit, this.m_SceneNode.getOrientation());
        if (this.m_fEmitAngle > 0)
        {
            var fEmitHalfAngle = this.m_fEmitAngle * 0.5;
            v2SpeedInit = rotateVector(v2SpeedInit, randomf(-fEmitHalfAngle, fEmitHalfAngle));
        }
        particle.m_v2Speed.x = v2SpeedInit.x * fSpeedInit;
        particle.m_v2Speed.y = v2SpeedInit.y * fSpeedInit;

        // Initialize angular speed
        var fAngularSpeed = randomf(this.m_fAngularSpeedMin, this.m_fAngularSpeedMax);
        particle.m_fAngularSpeed = fAngularSpeed;

        // Initialize scale
        var fScaleInit = randomf(this.m_fScaleMin, this.m_fScaleMax);
        particle.m_v2ScaleInit.x = fScaleInit;
        particle.m_v2ScaleInit.y = fScaleInit;
        particle.m_v2Scale.x = fScaleInit;
        particle.m_v2Scale.y = fScaleInit;

        // Initialize life
        var fLifeInit = randomf(this.m_fLifeMin, this.m_fLifeMax);
        particle.m_fLifeInit = fLifeInit;
        particle.m_fLife = fLifeInit;

        // Initialize angle
        var fAngleInit = randomf(this.m_fAngleMin, this.m_fAngleMax);
        particle.m_fOrientation = fAngleInit;

        particle.m_ParticleEmitter = this;
    }
}


/****************************************************************
 * Particle System
 ****************************************************************/

// Particle system
function ParticleSystem()
{
    //=======================
    // Fields
    //=======================

    this.m_aParticleEmitters =	new Array();
    this.m_aParticles =			new Array();
    this.m_Image =				null;
    this.m_ImageData =			null;
    this.m_iParticleCount =		0;
};


//=======================
// Accessors
//=======================

ParticleSystem.prototype.setImage = function(a_Image)
{
    this.m_Image = a_Image;
}

ParticleSystem.prototype.setParticleCount = function(a_ParticleCount)
{
    var iDiff = a_ParticleCount - this.m_iParticleCount;
    if (iDiff > 0)
    {
        for (var i = 0; i < iDiff; i++)
        {
            this.m_aParticles.push(new Particle());
        }
    }
    else if (iDiff < 0)
    {
        for (var i = 0; i < -iDiff; i++)
        {
            // TODO : save particle to remove
            if (this.m_aParticles.length > 0)
            {
                this.m_aParticles.splice(0, 1);
            }
        }
    }
    this.m_iParticleCount = a_ParticleCount;
}


//=======================
// Operations
//=======================

ParticleSystem.prototype.update = function()
{
    // Update particle emission
    var fdt = TimeEngine.getInstance().getDeltaTime();
    for (var i = 0; i < this.m_aParticleEmitters.length; i++)
    {
        var particleEmitter = this.m_aParticleEmitters[i];
        particleEmitter.emit(fdt);
    }

    // Update particle dynamic
    for (var i = 0; i < this.m_aParticleEmitters.length; i++)
    {
        var particleEmitter = this.m_aParticleEmitters[i];
        particleEmitter.update();
    }
}

ParticleSystem.prototype.addParticleEmitter = function()
{
    var particleEmitter = new ParticleEmitter();
    particleEmitter.m_ParticleSystem = this;
    this.m_aParticleEmitters.push(particleEmitter);
    return particleEmitter;
}

ParticleSystem.prototype.removeParticleEmitter = function(a_ParticleEmitter)
{
    for (var i = 0; i < this.m_aParticleEmitters.length; i++)
    {
        if (this.m_aParticleEmitters[i] == a_ParticleEmitter)
        {
            this.m_aParticleEmitters.splice(i, 1);
            return;
        }
    }
}

ParticleSystem.prototype.popParticle = function()
{
    if (this.m_aParticles.length > 0)
    {
        var particle = this.m_aParticles[0];
        this.m_aParticles.splice(0, 1);
        return particle;
    }
    return null;
}

ParticleSystem.prototype.pushParticle = function(a_Particle)
{
    this.m_aParticles.push(a_Particle);
}


/****************************************************************
 * Trail Particle
 ****************************************************************/

// Trail element
function TrailParticle()
{
    //=======================
    // Fields
    //=======================

    this.m_TrailEmitter =		null;
    this.m_v2PositionStart =	new b2Vec2(0, 0);
    this.m_v2PositionEnd =      new b2Vec2(0, 0);
    this.m_fOrientation =		0;
    this.m_fLifeInit =			1;
    this.m_fLife =				1;
    this.m_Color = 				"#ffaa00";
};


//=======================
// Operations
//=======================

TrailParticle.prototype.update = function(a_fdt)
{
    this.m_fLife -= a_fdt;
}


/****************************************************************
 * Trail Emitter
 ****************************************************************/

// Trail emitter
function TrailEmitter()
{
    //=======================
    // Fields
    //=======================

    this.m_TrailSystem =		    null;
    this.m_SceneNodeStart =		    new SceneNode();
    this.m_SceneNodeEnd =		    new SceneNode();
    this.m_aTrailParticles =		new Array();

    this.m_ColorStart =             new Color(255, 255, 255, 255);
    this.m_ColorEnd =               new Color(0, 0, 0, 0);

    this.m_fThickness =             10;

    this.m_fLife = 			        3;

    this.m_fFrequency =			    10;
    this.m_fTimeSinceLastPulse =    0;

    this.m_Blending =				"source-over";

    this.m_bScaleInTime =			false;

    this.m_bIsLaunched = 			false;
};


//=======================
// Accessors
//=======================

TrailEmitter.prototype.getSceneNodeStart = function()
{
    return this.m_SceneNodeStart;
}

TrailEmitter.prototype.getSceneNodeEnd = function()
{
    return this.m_SceneNodeEnd;
}

TrailEmitter.prototype.getColorStart = function()
{
    return this.m_ColorStart;
}

TrailEmitter.prototype.setColorStart = function(a_ColorStart)
{
    this.m_ColorStart = a_ColorStart;
}

TrailEmitter.prototype.getColorEnd = function()
{
    return this.m_ColorEnd;
}

TrailEmitter.prototype.setColorEnd = function(a_ColorEnd)
{
    this.m_ColorEnd = a_ColorEnd;
}

TrailEmitter.prototype.getThickness = function()
{
    return this.m_fThickness;
}

TrailEmitter.prototype.setThickness = function(a_fThickness)
{
    this.m_fThickness = a_fThickness;
}

TrailEmitter.prototype.getLife = function()
{
    return this.m_fLife;
}

TrailEmitter.prototype.setLife = function(a_fLife)
{
    this.m_fLife = a_fLife;
}

TrailEmitter.prototype.getFrequency = function()
{
    return this.m_fFrequency;
}

TrailEmitter.prototype.setFrequency = function(a_fFrequency)
{
    this.m_fFrequency = a_fFrequency;
}

TrailEmitter.prototype.getBlending = function()
{
    return this.m_Blending;
}

TrailEmitter.prototype.setBlending = function(a_Blending)
{
    this.m_Blending = a_Blending;
}

TrailEmitter.prototype.isScaleInTime = function()
{
    return this.m_bScaleInTime;
}

TrailEmitter.prototype.setScaleInTime = function(a_bScaleInTime)
{
    this.m_bScaleInTime = a_bScaleInTime;
}


//=======================
// Operations
//=======================

TrailEmitter.prototype.update = function()
{
    var fdt = TimeEngine.getInstance().getDeltaTime();

    // Remove killed particles
    var particleToRemove = new Array();
    for (var i = 0; i < this.m_aTrailParticles.length; i++)
    {
        var particle = this.m_aTrailParticles[i];
        if (particle.m_fLife < 0)
        {
            particleToRemove.push(particle);
            this.m_TrailSystem.pushParticle(particle);
        }
    }
    for (var i = 0; i < particleToRemove.length; i++)
    {
        var particle = particleToRemove[i];
        for (var j = 0; j < this.m_aTrailParticles.length; j++)
        {
            if (this.m_aTrailParticles[j] == particle)
            {
                this.m_aTrailParticles.splice(j, 1);
            }
        }
    }

    // Update particles
    for (var i = 0; i < this.m_aTrailParticles.length; i++)
    {
        var particle = this.m_aTrailParticles[i];
        particle.update(fdt);
    }

    // Draw particles
    this.draw();
}

TrailEmitter.prototype.draw = function()
{
    var v2CamPos = CameraEngine.getInstance().m_SceneNode.m_v2Pos;
    var v2PositionStart = new b2Vec2(0, 0);
    var v2PositionEnd = new b2Vec2(0, 0);
    var v2Dir = new b2Vec2(0, 0);
    var fLenght = 0;
    var fHalfLenght = 0;
    var fThickness = 0;
    var fOrientation = 0;
    var fRatio = 0;
    var f1mRatio = 0;

    // Draw particles
    for (var i = 0; i < this.m_aTrailParticles.length; i++)
    {
        var particle = this.m_aTrailParticles[i];

        v2PositionStart.x = particle.m_v2PositionStart.x;
        v2PositionStart.y = particle.m_v2PositionStart.y;
        v2PositionEnd.x = particle.m_v2PositionEnd.x;
        v2PositionEnd.y = particle.m_v2PositionEnd.y;

        var v2PosStartInScreen = convertScenePosToScreenPos(v2PositionStart, v2CamPos);
        var v2PosEndInScreen = convertScenePosToScreenPos(v2PositionEnd, v2CamPos);

        v2Dir.x = v2PositionEnd.x - v2PositionStart.x;
        v2Dir.y = v2PositionEnd.y - v2PositionStart.y;
        fLenght = Math.sqrt(v2Dir.x * v2Dir.x + v2Dir.y * v2Dir.y);

        fThickness = this.m_fThickness;
        fOrientation = particle.m_fOrientation;

        fRatio = particle.m_fLife / particle.m_fLifeInit;
        f1mRatio = 1 - fRatio;

        if (this.m_bScaleInTime)
        {
            fLenght = fLenght * fRatio;
            fThickness = fThickness * fRatio;
        }
        fHalfLenght = fLenght * 0.5;

        context.save();
        context.globalCompositeOperation = this.m_Blending;
        context.translate(v2PosStartInScreen.x - fHalfLenght, v2PosStartInScreen.y - fHalfLenght);
        context.rotate(fOrientation);
        var r = Math.floor(this.m_ColorEnd.r * f1mRatio + this.m_ColorStart.r * fRatio);
        var g = Math.floor(this.m_ColorEnd.g * f1mRatio + this.m_ColorStart.g * fRatio);
        var b = Math.floor(this.m_ColorEnd.b * f1mRatio + this.m_ColorStart.b * fRatio);
        var a = Math.floor(this.m_ColorEnd.a * f1mRatio + this.m_ColorStart.a * fRatio);
        context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
        context.fillRect(-0.5, -0.5, fThickness, fLenght);
        context.restore();
    }
}

TrailEmitter.prototype.launch = function()
{
    this.m_bIsLaunched = true;
}

TrailEmitter.prototype.stop = function()
{
    this.m_bIsLaunched = false;
}

TrailEmitter.prototype.emit = function(a_fdt)
{
    if (this.m_bIsLaunched)
    {
        this.m_fTimeSinceLastPulse += a_fdt;
        var iParticleCount = Math.floor(this.m_fTimeSinceLastPulse * this.m_fFrequency);
        if (iParticleCount > 0)
        {
            this.pulse(iParticleCount);
            this.m_fTimeSinceLastPulse -= iParticleCount / this.m_fFrequency;
        }
    }
}

TrailEmitter.prototype.pulse = function(a_iParticleCount)
{
    for (var i = 0; i < a_iParticleCount; i++)
    {
        var particle = this.m_TrailSystem.popParticle();
        if (particle == null)
        {
            return;
        }
        this.m_aTrailParticles.push(particle);

        // Initialize position
        particle.m_v2PositionStart.x = this.m_SceneNodeStart.getWorldPosition().x;
        particle.m_v2PositionStart.y = this.m_SceneNodeStart.getWorldPosition().y;
        particle.m_v2PositionEnd.x = this.m_SceneNodeEnd.getWorldPosition().x;
        particle.m_v2PositionEnd.y = this.m_SceneNodeEnd.getWorldPosition().y;

        // Initialize orientation
        var v2Dir = new b2Vec2(particle.m_v2PositionEnd.x - particle.m_v2PositionStart.x,
            particle.m_v2PositionEnd.y - particle.m_v2PositionStart.y);
        var fLenght = Math.sqrt(v2Dir.x * v2Dir.x + v2Dir.y * v2Dir.y);
        v2Dir.x = v2Dir.x / fLenght;
        v2Dir.y = v2Dir.y / fLenght;
        var fOrientation = Math.acos(v2Dir.x);
        if (v2Dir.y < 0.0)
        {
            fOrientation = 2 * Math.PI - fOrientation;
        }
        particle.m_fOrientation = fOrientation;

        // Initialize life
        particle.m_fLifeInit = this.m_fLife;
        particle.m_fLife = this.m_fLife;
    }
}


/****************************************************************
 * Trail System
 ****************************************************************/

// Trail system
function TrailSystem()
{
    //=======================
    // Fields
    //=======================

    this.m_aTrailEmitters =	    new Array();
    this.m_aTrailParticles =    new Array();
    this.m_Image =				null;
    this.m_ImageData =			null;
    this.m_iParticleCount =		0;
};


//=======================
// Accessors
//=======================

TrailSystem.prototype.setImage = function(a_Image)
{
    this.m_Image = a_Image;
}

TrailSystem.prototype.setParticleCount = function(a_ParticleCount)
{
    var iDiff = a_ParticleCount - this.m_iParticleCount;
    if (iDiff > 0)
    {
        for (var i = 0; i < iDiff; i++)
        {
            this.m_aTrailParticles.push(new TrailParticle());
        }
    }
    else if (iDiff < 0)
    {
        for (var i = 0; i < -iDiff; i++)
        {
            // TODO : save particle to remove
            if (this.m_aTrailParticles.length > 0)
            {
                this.m_aTrailParticles.splice(0, 1);
            }
        }
    }
    this.m_iParticleCount = a_ParticleCount;
}


//=======================
// Operations
//=======================

TrailSystem.prototype.update = function()
{
    // Update particle emission
    var fdt = TimeEngine.getInstance().getDeltaTime();
    for (var i = 0; i < this.m_aTrailEmitters.length; i++)
    {
        var trailEmitter = this.m_aTrailEmitters[i];
        trailEmitter.emit(fdt);
    }

    // Update particle dynamic
    for (var i = 0; i < this.m_aTrailEmitters.length; i++)
    {
        var trailEmitter = this.m_aTrailEmitters[i];
        trailEmitter.update();
    }
}

TrailSystem.prototype.addTrailEmitter = function()
{
    var trailEmitter = new TrailEmitter();
    trailEmitter.m_TrailSystem = this;
    this.m_aTrailEmitters.push(trailEmitter);
    return trailEmitter;
}

TrailSystem.prototype.removeTrailEmitter = function(a_TrailEmitter)
{
    for (var i = 0; i < this.m_aTrailEmitters.length; i++)
    {
        if (this.m_aTrailEmitters[i] == a_TrailEmitter)
        {
            this.m_aTrailEmitters.splice(i, 1);
            return;
        }
    }
}

TrailSystem.prototype.popParticle = function()
{
    if (this.m_aTrailParticles.length > 0)
    {
        var particle = this.m_aTrailParticles[0];
        this.m_aTrailParticles.splice(0, 1);
        return particle;
    }
    return null;
}

TrailSystem.prototype.pushParticle = function(a_Particle)
{
    this.m_aTrailParticles.push(a_Particle);
}
